home *** CD-ROM | disk | FTP | other *** search
- /* timeshift
- ** This program is linked to 3 different names.
- ** timeshift
- ** halt
- ** reboot
- ** When executed via the names halt or reboot, the clock is set to
- ** what it would be if the local time zone was GMT. After munging
- ** the clock, the real halt or reboot executable is then exec'ed.
- **
- ** When executed via the name timeshift, the clock is shifted back
- ** to the normal time within the local time zone.
- **
- ** In order to prevent shifting the clock an odd number of times
- ** and to prevent screwing up when shutdown and boot span a DST
- ** cusp, the amount of clock shift is stored in a lock file
- ** /usr/etc/shifted.time.
- **
- ** This software is provided as shareware.
- ** If you find this software useful, please send me $10
- **
- ** email feedback to jq@quick.com
- ** For current snail address refer to address returned from internic
- ** via 'whois quick.com'
- **
- ** $Header: /usr/users/jq/c/util/timeshift/RCS/timeshift.c,v 1.1 95/12/03 16:26:57 jq Exp $
- */
- #include <sys/param.h>
- #include <syslog.h>
- #include <libc.h>
- #include <sys/types.h>
- #include <sys/time.h>
- #include <sys/errno.h>
- #include <sys/file.h>
-
- #define TIME_LOCK "/usr/etc/shifted.time"
-
- extern int errno;
-
- enum shift_mode {
- UP = -1,
- NOCHANGE = 0,
- DOWN = 1
- };
-
- /* Return the basename of a pathname
- */
- char *
- basename(char *path)
- {
- char *base = rindex(path, '/');
-
- if (base) {
- base++;
- } else {
- base = path;
- }
-
- return base;
- }
-
- /* Shift the clock by the amount required to go from local time in time zone
- ** to local time in GMT when shutting down the computer. Reverse the
- ** process on the way back up.
- ** N.B. The reason for saving the time shift to a file and using it on the
- ** way back up, is to prevent dropping or gaining an hour if the system
- ** is brought down under daylight savings time and then brought up in
- ** standard time, or vice versa.
- */
- void
- shift_time(enum shift_mode mode)
- {
- struct timeval tp;
- struct timezone tzone;
- long shift = 0;
- int err;
- FILE *stamp = 0;
-
- /* Let someone know why we did nothing.
- */
- if (mode == NOCHANGE) {
- syslog(LOG_ALERT, "sync option prevented time change.");
- return;
- }
-
- /* Make sure that we do not set clock multiple timed in DOWN mode.
- */
- if (mode == DOWN && access(TIME_LOCK, F_OK) == 0) {
- syslog(LOG_ALERT, "%s already exists. No change made.",
- TIME_LOCK);
- return;
- }
-
- /* Open the TIME_LOCK file in read mode on the way up and in
- ** write mode on the way down.
- */
- stamp = fopen(TIME_LOCK, (mode == UP)?"r":"w");
- if (stamp == 0) {
- syslog(LOG_ALERT, "Cannot fopen %s - %s",
- TIME_LOCK,
- strerror(errno));
- return;
- }
-
- /* On the way down, set shift amount and save it in TIME_LOCK.
- ** On the way up, read the previous shift amount to undo it.
- */
- if (mode == DOWN) {
- time_t clock = 0;
- struct tm *now;
-
- time(&clock);
- now = localtime(&clock);
- fprintf(stamp, "%ld\n", now->tm_gmtoff);
- shift = mode * (now->tm_gmtoff);
- } else if (mode == UP) {
- fscanf(stamp, "%ld", &shift);
- shift *= mode;
- }
- fclose(stamp);
-
- /* Now, get the current time, adjust for shift, and reset time.
- ** Note that we are not accounting for the amount of time it
- ** will take to set the clock or performe the addition.
- ** Though we could probably perform the operation and get a
- ** rough guess of how many microseconds to fudge, I don't think
- ** it's worth the effort.
- */
- gettimeofday(&tp, &tzone);
- tp.tv_sec += shift;
- err = settimeofday(&tp, &tzone);
-
- if (err == -1) {
- /* If we failed to change the clock on the way down,
- ** then unlink the TIME_LOCK so that we don't make
- ** things worse on the way up.
- */
- if (mode == DOWN) {
- unlink(TIME_LOCK);
- }
- } else {
- /* If we did set the clock, get rid of our old stamp file
- ** since we've undone the change it represents.
- */
- if (mode == UP) {
- unlink(TIME_LOCK);
- }
- syslog(LOG_ALERT, "Clock adjusted by %d seconds\n", shift);
- }
- }
-
- /* Important safety checks!
- ** If 'reboot -n' or 'halt -n' are used it would be *extremely* uncool
- ** to do anything with the file system. Since a sync is not performed
- ** you can end up allocating a block which will remain linked to a
- ** directory entry, but actually be still on the free list. Ouch!
- **
- ** I do not know if 'reboot -q' suffers the same problems but I'm
- ** erring on false positive here and bailing on any command which
- ** has either 'n' or 'q' as any part of a hyphenated argument.
- */
- enum shift_mode
- safety_check(int argc, char **argv)
- {
- char *arg;
- int i;
-
- for (i = 0; i < argc; i++) {
- arg = argv[i];
- if (*arg == '-' && (index(arg, 'n') || index(arg, 'q'))) {
- return NOCHANGE; /* whew */
- }
- }
-
- return DOWN;
- }
-
- int
- main(int argc, char **argv)
- {
- char *base = basename(argv[0]);
- enum shift_mode mode = NOCHANGE;
- char realcmd[MAXPATHLEN + 1];
- char *exec_cmd = 0;
- char ishmael[128]; /* What to call me. */
-
-
- /* First determine whether we are coming down or booting up.
- */
- if (strcmp(base,"timeshift") == 0) {
- mode = UP;
- strcpy(ishmael, base);
- } else if (strcmp(base, "halt") == 0 || strcmp(base, "reboot") == 0) {
- char *prefix = "/usr/etc/";
-
- mode = safety_check(argc, argv);
-
- sprintf(ishmael, "%s (%s)", "timeshift", base);
- if (index(argv[0], '/') != 0) {
- prefix = "";
- }
- sprintf(realcmd, "%s%s.real", prefix, argv[0]);
-
- exec_cmd = realcmd;
- } else {
- syslog(LOG_ALERT, "%s: is not named reboot halt or shiftup\n", argv[0]);
- exit(1);
- }
- openlog(ishmael, LOG_CONS, LOG_DAEMON);
-
- /* Next make sure we are root.
- ** It's kind of gilding the lily a bit, but better safe than sorry.
- */
- if (geteuid() != 0) {
- syslog(LOG_ALERT, "%s: not run as root.\n", argv[0]);
- exit(1);
- }
-
- /* Cover our asses in case we should just not do anything.
- */
- shift_time(mode);
-
- /* If we were called instead of the real halt or reboot
- ** we now need to exec the real mcCoy.
- */
- if (exec_cmd) {
- execvp(exec_cmd, argv);
- syslog(LOG_ALERT, "Fatal error! Could't exec '%s'", realcmd);
- }
-
- return 0;
- }
-